package com.github.bryx.workflow.service.impl;

import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.bryx.workflow.domain.*;
import com.github.bryx.workflow.domain.process.runtime.TaskObject;
import com.github.bryx.workflow.dto.runtime.QuerySubworkflowInstanceDto;
import com.github.bryx.workflow.dto.runtime.QueryWorkflowTaskInstanceDto;
import com.github.bryx.workflow.service.WorkflowRuntimeQuery;
import com.github.bryx.workflow.service.dao.*;
import com.github.bryx.workflow.service.process.ProcessService;
import com.github.bryx.workflow.util.CollectionsUtil;
import com.google.common.base.Functions;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author jameswu
 * @Date 2021/6/10
 **/
@Service
public class WorkflowRuntimeQueryImpl implements WorkflowRuntimeQuery {

    @Autowired
    WorkflowInstanceDao workflowInstanceDao;

    @Autowired
    WorkflowTaskInstanceDao workflowTaskInstanceDao;

    @Autowired
    ProcessService processService;

    @Autowired
    WorkflowTimerJobDao workflowTimerJobDao;

    @Autowired
    WorkflowTimerInstanceDao workflowTimerInstanceDao;

    @Autowired
    WorkflowInstanceRelationDao workflowInstanceRelationDao;

    @Autowired
    WorkflowInstanceActivityDao workflowInstanceActivityDao;

    @Override
    public WorkflowInstance getWorkflowInstanceById(String id) {
        return workflowInstanceDao.getById(id);
    }

    @Override
    public WorkflowTaskInstance getWorkflowTaskInstanceById(String taskId) {
        WorkflowTaskInstance taskInstance = workflowTaskInstanceDao.getById(taskId);
        return taskInstance;
    }

    @Override
    public String getProcessDefKeyOfWorkflowInstance(String workflowInstanceId) {
        return workflowInstanceDao.getProcessDefKeyOfWorkflowInstance(workflowInstanceId);
    }

    @Override
    public Page<WorkflowTaskInstance> queryWorkflowTaskInstances(QueryWorkflowTaskInstanceDto<WorkflowTaskInstance> dto) {
        LambdaQueryChainWrapper<WorkflowTaskInstance> queryChainWrapper = workflowTaskInstanceDao.lambdaQuery();
        if (CollectionsUtil.isNotEmpty(dto.getStatuses())){
            queryChainWrapper = queryChainWrapper.in(WorkflowTaskInstance::getStatus, dto.getStatuses());
        }
        if (CollectionsUtil.isNotEmpty(dto.getTaskDefIds())){
            queryChainWrapper = queryChainWrapper.in(WorkflowTaskInstance::getProcessTaskDefId, dto.getTaskDefIds());
        }
        if (CollectionsUtil.isNotEmpty(dto.getWorkflowInstanceIds())){
            queryChainWrapper = queryChainWrapper.in(WorkflowTaskInstance::getWorkflowInstanceId, dto.getWorkflowInstanceIds());
        }
        if (CollectionsUtil.isNotEmpty(dto.getWorkflowTaskInstanceIds())){
            queryChainWrapper = queryChainWrapper.in(WorkflowTaskInstance::getId, dto.getWorkflowTaskInstanceIds());
        }

        if (dto.getOrderField()!=null){
            queryChainWrapper = queryChainWrapper.orderBy(true, dto.getAsc(), dto.getOrderField());
        }
        Page<WorkflowTaskInstance> page = queryChainWrapper.page(dto.page());
        List<WorkflowTaskInstance> workflowTaskInstances = page.getRecords();
        if (CollectionsUtil.isNotEmpty(workflowTaskInstances)){
            List<WorkflowTaskInstance> ongoingTasks = workflowTaskInstances.stream().filter(inst -> inst.getStatus().equals(WorkflowTaskInstance.WorkflowTaskInstanceStatus.RUNNING)).collect(Collectors.toList());
            Map<String, TaskObject> idToTaskObject = processService.getTasks(ongoingTasks.stream().map(WorkflowTaskInstance::getProcessTaskId).collect(Collectors.toList()))
                    .stream().collect(Collectors.toMap(TaskObject::getId, Functions.identity()));
            ongoingTasks.forEach(inst->{
                inst.setAssigneeUserIds(idToTaskObject.get(inst.getProcessTaskId()).getAssignedUserIds());
                inst.setAssigneeGroupIds(idToTaskObject.get(inst.getProcessTaskId()).getAssignedGroupIds());
            });
        }
        return page;
    }


    @Override
    public List<WorkflowTimerJob> getWorkflowTimerJobsOnTask(String workflowInstanceTaskId) {
        List<WorkflowTimerJob> workflowTimerJobs = workflowTimerJobDao.lambdaQuery().eq(WorkflowTimerJob::getWorkflowTaskInstanceId, workflowInstanceTaskId).list();
        return workflowTimerJobs;
    }

    @Override
    public List<WorkflowTimerJob> getWorkflowTimerJobsOnWorkflow(String workflowInstanceId) {
        List<WorkflowTimerJob> workflowTimerJobs = workflowTimerJobDao.lambdaQuery().eq(WorkflowTimerJob::getWorkflowInstanceId, workflowInstanceId).list();
        return workflowTimerJobs;
    }

    @Override
    public WorkflowTimerJob getWorkflowTimerByJobId(String processTimerJobId) {
        WorkflowTimerJob workflowTimerJob = workflowTimerJobDao.lambdaQuery().eq(WorkflowTimerJob::getProcessTimerJobId, processTimerJobId).one();
        return workflowTimerJob;
    }

    @Override
    public List<WorkflowTimerInstance> getWorkflowTimerInstancesOnTask(String workflowInstanceTaskId) {
        List<WorkflowTimerInstance> workflowTimerInstances = workflowTimerInstanceDao.lambdaQuery().eq(WorkflowTimerInstance::getWorkflowTaskId, workflowInstanceTaskId).list();
        return workflowTimerInstances;
    }

    @Override
    public List<WorkflowInstance> querySubworkflowInstances(QuerySubworkflowInstanceDto querySubworkflowInstanceDto) {
        List<String> workflowInstanceIds = Lists.newArrayList();

        if (CollectionsUtil.isNotEmpty(querySubworkflowInstanceDto.getParentWorkflowInstanceIds())
                || CollectionsUtil.isNotEmpty(querySubworkflowInstanceDto.getParentWorkflowTaskInstanceIds())
                || CollectionsUtil.isNotEmpty(querySubworkflowInstanceDto.getParentProcessTaskDefIds())){
            LambdaQueryChainWrapper<WorkflowInstanceRelation> workflowInstanceRelationLambdaQueryChainWrapper = workflowInstanceRelationDao.lambdaQuery();
            if (CollectionsUtil.isNotEmpty(querySubworkflowInstanceDto.getParentWorkflowInstanceIds())){
                workflowInstanceRelationLambdaQueryChainWrapper = workflowInstanceRelationLambdaQueryChainWrapper
                        .in(WorkflowInstanceRelation::getWorkflowInstanceId, querySubworkflowInstanceDto.getParentWorkflowInstanceIds());
            }
            if (CollectionsUtil.isNotEmpty(querySubworkflowInstanceDto.getParentWorkflowTaskInstanceIds())){
                workflowInstanceRelationLambdaQueryChainWrapper = workflowInstanceRelationLambdaQueryChainWrapper
                        .in(WorkflowInstanceRelation::getWorkflowTaskInstanceId, querySubworkflowInstanceDto.getParentWorkflowTaskInstanceIds());
            }
            if (CollectionsUtil.isNotEmpty(querySubworkflowInstanceDto.getParentProcessTaskDefIds())){
                workflowInstanceRelationLambdaQueryChainWrapper = workflowInstanceRelationLambdaQueryChainWrapper
                        .in(WorkflowInstanceRelation::getProcessTaskDefId, querySubworkflowInstanceDto.getParentProcessTaskDefIds());
            }
            workflowInstanceIds = workflowInstanceRelationLambdaQueryChainWrapper.list().stream().map(WorkflowInstanceRelation::getSubWorkflowInstanceId).collect(Collectors.toList());
        }

        LambdaQueryChainWrapper<WorkflowInstance> workflowInstanceLambdaQueryChainWrapper = workflowInstanceDao.lambdaQuery();
        if (CollectionsUtil.isNotEmpty(workflowInstanceIds)){
            workflowInstanceLambdaQueryChainWrapper = workflowInstanceLambdaQueryChainWrapper.in(WorkflowInstance::getId, workflowInstanceIds);
        }
        if (CollectionsUtil.isNotEmpty(querySubworkflowInstanceDto.getStatuses())){
            workflowInstanceLambdaQueryChainWrapper = workflowInstanceLambdaQueryChainWrapper.in(WorkflowInstance::getStatus, querySubworkflowInstanceDto.getStatuses());
        }
        return workflowInstanceLambdaQueryChainWrapper.list();
    }

    @Override
    public List<WorkflowInstanceActivity> queryWorkflowInstActivity(String workflowInstanceId) {
        return workflowInstanceActivityDao.lambdaQuery().eq(WorkflowInstanceActivity::getWorkflowInstanceId, workflowInstanceId).list();
    }
}
